package org.zionchina.cross.server;


import java.net.InetAddress;
import java.net.URL;
import java.util.ArrayList;

import java.io.IOException;

import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.Session;
import org.apache.catalina.connector.Connector;

import org.apache.catalina.realm.MemoryRealm;
import org.apache.catalina.startup.Embedded;
import org.apache.tomcat.util.IntrospectionUtils;
import org.eclipse.core.runtime.FileLocator;

//import org.eclipse.tomcat.internal.TomcatPlugin;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;


import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;


/**
 * @author placson
 */
public class Tomcat55Plugin extends Plugin implements BundleActivator {
	private static boolean running = false;


	private static Embedded embedded = new Embedded();

	private static Host host = null;

	private static Context rootcontext;

	/**
	 * Add ROOT context
	 * 
	 */
	public final static String PLUGIN_ID = "org.zionchina.cross.Crossbase"; //$NON-NLS-1$

	private static Tomcat55Plugin plugin;

	// private static BundleContext bundleContext;

	public static boolean addRoot() {
		host.addChild(rootcontext);
		return true;
	}

	public static boolean createContext(String contextPath, String absolutePath)
			throws Exception {
		Context context = embedded.createContext(contextPath, absolutePath);
		context.setReloadable(false);
		host.addChild(context);
		System.out.println("done");
		return true;
	}

	public static Tomcat55Plugin getDefault() {
		return plugin;
	}

	/**
	 * Get Application scope variable
	 * 
	 * @param name
	 *            Application variable name
	 * @return Application variable value
	 */
	public static Object getRootContextAttribute(String name) {
		return rootcontext.getServletContext().getAttribute(name);
	}

	/**
	 * Get path of web applcation.
	 * 
	 * @param pluginId
	 *            Embedded web application id
	 * @param path
	 *            webapp path relative to the plugin directory
	 * @return String absolute webapp path
	 * @exception CoreException
	 */
	public static String getWebappPath(String pluginId) throws CoreException {
		Bundle bundle = Platform.getBundle(pluginId);

		if (bundle == null) {
			throw new CoreException(new Status(IStatus.ERROR, "", IStatus.OK,
					"cannot find Plugin", null));
		}

		// Note: we just look for one webapp directory.
		// If needed, may want to use the locale specific path.
		URL webappURL = Platform.find(bundle, new Path("/"));

		if (webappURL == null) {
			throw new CoreException(new Status(IStatus.ERROR, "", IStatus.OK,
					"cannot Find Path", null));
		}

		try {
			String webappLocation = Platform.asLocalURL(
					Platform.resolve(webappURL)).getFile();

			return webappLocation;
		} catch (IOException ioe) {

			// TODO: need to handle resource
			throw new CoreException(new Status(IStatus.ERROR, "", IStatus.OK,
					"cannot resolve path", ioe));
		}
	}

	public static boolean isRunning() {
		return running;
	}

	public static boolean loadModule(String webappName, String pluginId) {
		try {
			
			if (!Tomcat55Plugin.isRunning()) {

				startTomcat();
				

			}
			String path = getWebappPath(pluginId) + "/webapps/ROOT/";
			//System.out.println(path);
			// createContext(webappName,path );
			if (!webappName.startsWith("/")) { //$NON-NLS-1$
				webappName = "/" + webappName; //$NON-NLS-1$
			}
			
			Context context = embedded.createContext(webappName, path);

			WebAppClassLoader webappLoader = new WebAppClassLoader(
					new PluginClassLoaderWrapper(pluginId));
			
			context.setLoader(embedded.createLoader(webappLoader));
			//System.out.println("test2");
			host.addChild(context);
			//System.out.println("test");
		} catch (Exception e) {
			e.printStackTrace();
		}

		return true;
	}

	/**
	 * Logs an Error message with an exception. Note that the message should
	 * already be localized to proper locale. ie: TomcatResources.getString()
	 * should already have been called
	 */
	public static synchronized void logError(String message, Throwable ex) {
		if (message == null)
			message = ""; //$NON-NLS-1$
		Status errorStatus = new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK,
				message, ex);
		Tomcat55Plugin.getDefault().getLog().log(errorStatus);
	}

	public static void main(String[] args) {
		Tomcat55Plugin tomcat = new Tomcat55Plugin();
		/* jdk15 verify */
		// java.util.LinkedList<Integer> list = new
		// java.util.LinkedList<Integer>();
		// tomcat.setPath(new File(".").getAbsolutePath());
		try {
			tomcat.startTomcat();

			// unload root after 5 seconds
			Thread.currentThread().sleep(5 * 1000);
			tomcat.removeRoot();
			//System.out.println("Root context removed!");

			// wait another 5 seconds and add again
			Thread.currentThread().sleep(5 * 1000);
			tomcat.addRoot();
			//System.out.println("Root context added!");
			// System.out.println(tomcat.getPath()+"/webapps/jsf");
			// tomcat.createContext("jsf","C:\\workspace\\cross\\TomSever\\webapps\\jsf");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Registers a WAR with the container.
	 * 
	 * @param contextPath -
	 *            the context path under which the application will be
	 *            registered
	 * @param warFile -
	 *            the URL of the WAR to be registered.
	 */
	public static boolean registerWAR(String contextPath, String absolutePath)
			throws Exception {
		Context context = embedded.createContext(contextPath, absolutePath);
		context.setReloadable(false);
		host.addChild(context);
		return true;
	}

	/**
	 * Refresh ROOT context
	 * 
	 */
	public static boolean reloadRoot() {
		rootcontext.reload();
		return true;
	}

	/**
	 * Remove ROOT context
	 * 
	 */
	public static boolean removeRoot() {
		host.removeChild(rootcontext);
		return true;
	}

	/**
	 * Remove Application scope variable
	 * 
	 * @param name
	 *            Application variable name
	 */
	public static boolean removeRootContextAttribute(String name) {
		rootcontext.getServletContext().removeAttribute(name);
		return true;
	}

	/**
	 * Basic Accessor setting the value of the context path
	 * 
	 * @param path -
	 *            the path
	 */
	public static boolean setPath(String path) {

		path = path;
		return true;
	}

	/**
	 * Basic Accessor returning the value of the context path
	 * 
	 * @return - the context path
	 */
	/*
	 * public String getPath() { return this.path; }
	 */

	/**
	 * Set Application scope variable
	 * 
	 * @param name
	 *            Application variable name
	 * @param obj
	 *            Application variable value
	 */
	public static boolean setRootContextAttribute(String name, Object obj) {
		rootcontext.getServletContext().setAttribute(name, obj);
		return true;
	}

	/**
	 * Set Session scope variable
	 * 
	 * @param name
	 *            Session variable name
	 * @param obj
	 *            Session variable value
	 */
	public static boolean setRootContextSessionAttribute(String name, Object obj) {
		try {
			Session sessions[] = rootcontext.getManager().findSessions();

			for (int i = 0, size = sessions.length; i < size; i++) {
				sessions[i].getSession().setAttribute(name, obj);
			}
		} catch (Exception x) {
		}
		return true;
	}

	public static void setRunning(boolean running) {
		Tomcat55Plugin.running = running;
	}

	/**
	 * This method Starts the Tomcat server.
	 */
	public static boolean startTomcat() throws Exception {
		Engine engine = null;

		// Create an embedded server

		embedded.setRealm(new MemoryRealm());
		// embedded.setDebug(0);
		// embedded.setLogger(logger);
		URL installURL = Platform
				.getBundle("org.zionchina.cross").getEntry("/"); //$NON-NLS-1$
		URL resolvedURL = FileLocator.resolve(installURL);
		String home = FileLocator.toFileURL(resolvedURL).getFile();
		System.setProperty("catalina.home", home); //$NON-NLS-1$
		String base = home;
		System.setProperty("catalina.base", base); //$NON-NLS-1$

		// Create an engine
		engine = embedded.createEngine();
		engine.setDefaultHost("localhost");

		// Create a default virtual host
		host = embedded.createHost("localhost", home + "/webapps");
		engine.addChild(host);
		
		embedded.addEngine(engine);

		int port = 8080;
		String addr = null;
		Connector connector = null;// this.embedded.createConnector(addr, port,
		// false);
		/*
		 * embedded.createConnector(...) seems to be broken.. it always returns
		 * a null connector. see work around below
		 */
		InetAddress address = null;
		try {
			connector = new Connector();
			// httpConnector.setScheme("http");
			connector.setSecure(false);
			connector.setUseBodyEncodingForURI(true);
			address = InetAddress.getLocalHost();
			if (address != null) {
				IntrospectionUtils.setProperty(connector, "address", ""
						+ address);
			}
			IntrospectionUtils.setProperty(connector, "port", "" + port);

		} catch (Exception ex) {
			ex.printStackTrace();
		}
		connector.setEnableLookups(false);

		embedded.addConnector(connector);
		// Start the embedded server
		embedded.start();
		setRunning(true);
		return true;
	}

	/**
	 * This method Stops the Tomcat server.
	 */
	public static boolean stopTomcat() throws Exception {
		// Stop the embedded server
		embedded.stop();
		return true;
	}

	/**
	 * Unregisters a WAR from the web server.
	 * 
	 * @param contextPath -
	 *            the context path to be removed
	 */
	public static boolean unregisterWAR(String contextPath) throws Exception {

		Context context = host.map(contextPath);
		if (context != null) {
			embedded.removeContext(context);
		} else {
			throw new Exception("Context does not exist for named path : "
					+ contextPath);
		}
		return true;
	}

	private ArrayList contexts = new ArrayList();

	private String path = null;

	public Tomcat55Plugin() {
	}

	public void start(BundleContext context) throws Exception {
		super.start(context);
		plugin = this;

	}

	public void stop(BundleContext context) throws Exception {

		stopTomcat();

		plugin = null;
		// bundleContext = null;
		super.stop(context);
	}

	/**
	 * @see org.eclipse.help.internal.appserver.IWebappServer#stop(java.lang.String)
	 */
	public synchronized void stop(String webappName) throws CoreException {
		if (!running) {
			return;
		}
		Context context = (Context) host.findChild("/" + webappName); //$NON-NLS-1$
		if (context != null) {
			contexts.remove(context);
			embedded.removeContext(context);
		}
	}
}